#include "convolution.h"
#include "math.h"
#include <QDebug>

void compute(QImage &src, QImage &dst, int *operatorX,
             int *operatorY, int size, GradientMode mode)
{
    int width = src.width();
    int height = src.height();

    for (int i = 0; i < height; i++)
    {
        uchar* srcPtr = src.scanLine(i);
        uchar* dstPtr = dst.scanLine(i);
        for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
        {
            int* xx = new int[size];
            int* yy = new int[size];
            if (size == 2)
            {
                for (int k = 0; k < size; k++)
                {
                    xx[k] = k;
                    yy[k] = k;
                }
                if (j + 1 >= width)
                {
                    xx[1] = 0;
                }
                if (i + 1 >= height)
                {
                    yy[1] = 0;
                }
            }
            else if (size == 3)
            {
                for (int k = 0; k < size; k++)
                {
                    xx[k] = k - 1;
                    yy[k] = k - 1;
                }
                if (j - 1 < 0)
                {
                    xx[0] = 0;
                }
                if (i - 1 < 0)
                {
                    yy[0] = 0;
                }
                if (j + 1 >= width)
                {
                    xx[2] = 0;
                }
                if (i + 1 >= height)
                {
                    yy[2] = 0;
                }
            }

            int gx = 0, gy = 0;
            int count = 0;
            for (int m = 0; m < size; m++)
            {
                int wOffset = yy[m] * width;
                for (int n = 0; n < size; n++)
                {
                    uchar value = *(srcPtr + wOffset + xx[n]);
                    gx += operatorX[count] * value;
                    gy += operatorY[count] * value;
                    count++;
                }
            }
            int g = (int)sqrt((double(gx * gx + gy * gy)));
            switch(mode)
            {
            case HORIZONTAL:
                g = gx;
                break;
            case VERTICAL:
                g = gy;
                break;
            case ALL:
                break;
            }
            if (g >= 255)
            {
                *dstPtr = 255;
            }
            else
            {
                *dstPtr = g;
            }
        }
    }
}

QImage applyRobertsOperator(QImage &image, GradientMode mode)
{
    int robertsOperatorX[4] = {-1, 0,
                               0, 1};
    int robertsOperatorY[4] = {0, -1,
                               1, 0};

    QImage afterImage = QImage(image.size(), QImage::Format_Indexed8);

    compute(image, afterImage, robertsOperatorX, robertsOperatorY, 2, mode);

    return afterImage;
}

QImage applyPrewittOperator(QImage &image, GradientMode mode)
{
    int prewittOperatorX[9] = {-1, -1, -1,
                               0, 0, 0,
                               1, 1, 1};
    int prewittOperatorY[9] = {-1, 0, 1,
                               -1, 0, 1,
                               -1, 0, 1};

    QImage afterImage = QImage(image.size(), QImage::Format_Indexed8);

    compute(image, afterImage, prewittOperatorX, prewittOperatorY, 3, mode);

    return afterImage;
}

QImage applySobelOperator(QImage &image, GradientMode mode)
{
    int sobelOperatorX[9] = {-1, -2, -1,
                             0, 0, 0,
                             1, 2, 1};
    int sobelOperatorY[9] = {-1, 0, 1,
                             -2, 0, 2,
                             -1, 0, 1};

    QImage afterImage = QImage(image.size(), QImage::Format_Indexed8);

    compute(image, afterImage, sobelOperatorX, sobelOperatorY, 3, mode);

    return afterImage;
}

QImage applyMeanFilter(QImage &image)
{
    int width = image.width();
    int height = image.height();

    QImage afterImage = QImage(image);

    if (image.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < height; i++)
        {
            uchar* srcPtr = image.scanLine(i);
            uchar* dstPtr = afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[3];
                int* yy = new int[3];
                int temp = 3 / 2;
                for (int k = 0; k < 3; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                int tempPixel = 0;
                for (int m = 0; m < 3; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < 3; n++)
                    {
                        tempPixel += *(srcPtr + wOffset + xx[n]);
                    }
                }
                *dstPtr = tempPixel / 9;
            }
        }
    }
    else
    {
        for (int i = 0; i < height; i++)
        {
            QRgb* srcPtr = (QRgb*)image.scanLine(i);
            QRgb* dstPtr = (QRgb*)afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[3];
                int* yy = new int[3];
                int temp = 3 / 2;
                for (int k = 0; k < 3; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                int tempA = 0;
                int tempR = 0;
                int tempG = 0;
                int tempB = 0;
                for (int m = 0; m < 3; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < 3; n++)
                    {
                        QRgb rgb = *(srcPtr + wOffset + xx[n]);
                        tempA += qAlpha(rgb);
                        tempR += qRed(rgb);
                        tempG += qGreen(rgb);
                        tempB += qBlue(rgb);
                    }
                }
                *dstPtr = qRgba(tempR / 9, tempG / 9, tempB / 9, tempA / 9);
            }
        }
    }
    return afterImage;
}

QImage applyMedianFilter(QImage &image)
{
    int width = image.width();
    int height = image.height();

    QImage afterImage = QImage(image);

    if (image.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < height; i++)
        {
            uchar* srcPtr = image.scanLine(i);
            uchar* dstPtr = afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[3];
                int* yy = new int[3];
                int temp = 3 / 2;
                for (int k = 0; k < 3; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                uchar* tempArray = new uchar[9];
                int tempCount = 0;
                for (int m = 0; m < 3; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < 3; n++)
                    {
                        *(tempArray + tempCount) = *(srcPtr + wOffset + xx[n]);
                        tempCount++;
                    }
                }
                *dstPtr = findMedian(tempArray, 9);
            }
        }
    }
    else
    {
        for (int i = 0; i < height; i++)
        {
            QRgb* srcPtr = (QRgb*)image.scanLine(i);
            QRgb* dstPtr = (QRgb*)afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[3];
                int* yy = new int[3];
                int temp = 3 / 2;
                for (int k = 0; k < 3; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                uchar* tempA = new uchar[9];
                uchar* tempR = new uchar[9];
                uchar* tempG = new uchar[9];
                uchar* tempB = new uchar[9];
                int tempCount = 0;
                for (int m = 0; m < 3; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < 3; n++)
                    {
                        QRgb rgb = *(srcPtr + wOffset + xx[n]);
                        *(tempA + tempCount) = qAlpha(rgb);
                        *(tempR + tempCount) = qRed(rgb);
                        *(tempG + tempCount) = qGreen(rgb);
                        *(tempB + tempCount) = qBlue(rgb);
                        tempCount++;
                    }
                }
                *dstPtr = qRgba(findMedian(tempR, 9),
                                findMedian(tempG, 9),
                                findMedian(tempB, 9),
                                findMedian(tempA, 9));
            }
        }
    }
    return afterImage;
}

uchar findMedian(uchar *srcs, int size)
{
    for (int i = 0; i < size; i++)
    {
        for (int j = i + 1; j < size; j++)
        {
            if (*(srcs + i) > *(srcs + j))
            {
                uchar temp = *(srcs + i);
                *(srcs + i) = *(srcs + j);
                *(srcs + j) = temp;
            }
        }
    }
    return *(srcs + size / 2);
}

QImage applySpherize(qreal radius, qreal height, QImage src)
{
    QImage after = QImage(src);
    int img_width = after.width();
    int img_height = after.height();
    int centerX = img_width / 2;
    int centerY = img_height / 2;

    qreal squareRadius = radius * radius;
    qreal diffHeight = radius - height;
    qreal squareDiffHeight = diffHeight * diffHeight;
    qreal projRadius = sqrt(squareRadius - squareDiffHeight);

    int leftX = centerX - projRadius;
    int rightX = centerX + projRadius;
    int upY = centerY - projRadius;
    int downY = centerY + projRadius;
    leftX = leftX < 0 ? 0 : leftX;
    rightX = rightX >= img_width ? img_width - 1 : rightX;
    upY = upY < 0 ? 0 : upY;
    downY = downY >= img_height ? img_height - 1 : downY;

    if (after.format() == QImage::Format_Indexed8)
    {
        uchar* srcPtr = src.bits();
        uchar* dstPtr = after.bits();

        dstPtr += upY * img_width + leftX;
        for (int i = upY; i <= downY; i++)
        {
            int realY = centerY - i;
            int squareY = realY * realY;
            int tempSquare = squareY + squareDiffHeight;
            int dis = squareRadius - tempSquare;
            for (int j = leftX; j <= rightX; j++, dstPtr++)
            {
                int realX = j - centerX;
                int squareX = realX * realX;
                if (squareX < dis)
                {
                    qreal r = sqrt(squareX + squareY);
                    qreal temp = diffHeight / sqrt(squareRadius - r * r);
                    int x = realX * temp;
                    int y = realY * temp;
                    x += centerX;
                    y = centerY - y;
                    x = x < 0 ? 0 : x;
                    x = x > img_width ? (img_width - 1) : x;
                    y = y < 0 ? 0 : y;
                    y = y > img_height ? (img_height - 1) : y;
                    *dstPtr = *(srcPtr + y * img_width + x);
                }
            }
            dstPtr += img_width - rightX + leftX - 1;
        }
    }
    else
    {
        QRgb* dstPtr = (QRgb*)after.bits();

        dstPtr += upY * img_width + leftX;
        for (int i = upY; i <= downY; i++)
        {
            int realY = centerY - i;
            int squareY = realY * realY;
            int tempSquare = squareY + squareDiffHeight;
            int dis = squareRadius - tempSquare;
            for (int j = leftX; j <= rightX; j++, dstPtr++)
            {
                int realX = j - centerX;
                int squareX = realX * realX;
                if (squareX < dis)
                {
                    qreal r = sqrt(squareX + squareY);
                    qreal temp = diffHeight / sqrt(squareRadius - r * r);
                    int x = realX * temp;
                    int y = realY * temp;
                    x += centerX;
                    y = centerY - y;
                    x = x < 0 ? 0 : x;
                    x = x > img_width ? (img_width - 1) : x;
                    y = y < 0 ? 0 : y;
                    y = y > img_height ? (img_height - 1) : y;
                    *dstPtr = src.pixel(x, y);
                }
            }
            dstPtr += img_width - rightX + leftX - 1;
        }
    }
    return after;
}

QImage applyPolarToRectangular(QImage src)
{
    int width = src.width();
    int height = src.height();
    int originX = width / 2;
    int originY = height / 2;

    int R = qMin(width, height) / 2;

    QImage dst = QImage(src);
    if (src.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < height; i++)
        {
            uchar* dstPtr = dst.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++)
            {
                double angle = (double)j * 2 * PI / (double)width;
                double radius = (double)i * R / (double)height;
                int x = radius * cos(angle);
                int y = radius * sin(angle);

                int realX = originX - y;
                int realY = height - originY - x - 1;

                realX = realX < 0 ? 0 : realX;
                realY = realY < 0 ? 0 : realY;
                realX = realX >= width ? width - 1 : realX;
                realY = realY >= height ? height - 1 : realY;

                *dstPtr = *(src.scanLine(realY) + realX);
            }
        }
    }
    else
    {
        for (int i = 0; i < height; i++)
        {
            QRgb* dstPtr = (QRgb*)dst.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++)
            {
                double angle = (double)j * 2 * PI / (double)width;
                double radius = (double)i * R / (double)height;
                int x = radius * cos(angle);
                int y = radius * sin(angle);

                int realX = originX - y;
                int realY = height - originY - x - 1;

                realX = realX < 0 ? 0 : realX;
                realY = realY < 0 ? 0 : realY;
                realX = realX >= width ? width - 1 : realX;
                realY = realY >= height ? height - 1 : realY;

                *dstPtr = src.pixel(realX, realY);
            }
        }
    }
    return dst;
}

QImage applyRectangularToPolar(QImage src)
{
    int width = src.width();
    int height = src.height();
    int originX = width / 2;
    int originY = height / 2;

    int R = qMin(width, height) / 2;

    QImage dst = QImage(src);
    if (src.format() == QImage::Format_Indexed8)
    {
        uchar* dstPtr = dst.bits();
        for (int i = 0; i < width; i++)
        {
            int y = originX - i;
            for (int j = 0; j < height; j++)
            {
                int x = j - originY;
                double radius = sqrt(x * x + y * y);
                double angle = acos((double)x / radius);
                if (y < 0)
                {
                    angle = 2 * PI - angle;
                }
                int realY = (int)(radius * height / R);
                int realX = (int)(angle * width / (2 * PI));
                realX = realX < 0 ? 0 : realX;
                realY = realY < 0 ? 0 : realY;
                realX = realX >= width ? width - 1 : realX;
                realY = realY >= height ? height - 1 : realY;

                *(dstPtr + i + (height - j - 1) * width) = *(src.scanLine(realY) + realX);
            }
        }
    }
    else
    {
        for (int i = 0; i < width; i++)
        {
            int y = originX - i;
            for (int j = 0; j < height; j++)
            {
                int x = j - originY;
                double radius = sqrt(x * x + y * y);
                double angle = 0;
                if (radius != 0)
                {
                    angle = acos((double)x / radius);
                }
                if (y < 0)
                {
                    angle = 2 * PI - angle;
                }
                int realY = (int)(radius * height / R);
                int realX = (int)(angle * width / (2 * PI));
                realX = realX < 0 ? 0 : realX;
                realY = realY < 0 ? 0 : realY;
                realX = realX >= width ? width - 1 : realX;
                realY = realY >= height ? height - 1 : realY;

                dst.setPixel(i, height - j - 1, src.pixel(realX, realY));
            }
        }
    }
    return dst;
}

QImage applyWhirlix(int degree, QImage src)
{
    int width = src.width();
    int height = src.height();
    int originX = width / 2;
    int originY = height / 2;

    double length = qMin(originX, originY);

    double b = degree * PI / 180.0;

    QImage dst = QImage(src);
    if (src.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < height; i++)
        {
            int y = originY - i;
            uchar* dstPtr = dst.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++)
            {
                int x = j - originX;
                double radius = sqrt(x * x + y * y);
                double angle = 0;
                if (radius != 0)
                {
                    angle = acos((double)x / radius);
                }
                if (y < 0)
                {
                    angle = 2 * PI - angle;
                }

                double alpha = log(radius / length) * b;

                int realX = 0, realY = 0;
                if (radius < length)
                {
                    realX = radius * cos(angle - alpha) + originX;
                    realY = originY - radius * sin(angle - alpha);

                    realX = realX < 0 ? 0 : realX;
                    realY = realY < 0 ? 0 : realY;
                    realX = realX >= width ? width - 1 : realX;
                    realY = realY >= height ? height - 1 : realY;
                }
                else
                {
                    realX = j;
                    realY = i;
                }

                *dstPtr = *(src.scanLine(realY) + realX) ;
            }
        }
    }
    else
    {
        for (int i = 0; i < height; i++)
        {
            int y = originY - i;
            QRgb* dstPtr = (QRgb*)dst.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++)
            {
                int x = j - originX;
                double radius = sqrt(x * x + y * y);
                double angle = 0;
                if (radius != 0)
                {
                    angle = acos((double)x / radius);
                }
                if (y < 0)
                {
                    angle = 2 * PI - angle;
                }

                double alpha = log(radius / length) * b;

                int realX = 0, realY = 0;
                if (radius < length)
                {
                    realX = radius * cos(angle - alpha) + originX;
                    realY = originY - radius * sin(angle - alpha);

                    realX = realX < 0 ? 0 : realX;
                    realY = realY < 0 ? 0 : realY;
                    realX = realX >= width ? width - 1 : realX;
                    realY = realY >= height ? height - 1 : realY;
                }
                else
                {
                    realX = j;
                    realY = i;
                }

                *dstPtr = src.pixel(realX, realY);
            }
        }
    }
    return dst;
}

QImage applyWave(uint waveLength, uint amplitude, double phase ,uint type, QImage src)
{
    // our goal is to calculate each pixel in desImage (dstImage_x,dstImage_y) , and map the point to srcImage
    QImage after = QImage(src);		// dstImage
    int srcImage_x, srcImage_y;	// srcImage pixel x , y
    int width = after.width();		// Image width
    int height = after.height();	// Image height
    //uchar *srcPtr = src.bits();         // pointer to srcImage Data
    //uchar *dstPtr = after.bits();       // pointer to dstImage Data

    if (src.format() == QImage::Format_Indexed8)
    {
        uchar* srcPtr = src.bits();

        int i,j;

        // function proto f(x) = Asin(wx + fi)
        uint A = amplitude;
        assert(waveLength > 0);
        double w = 2*PI/waveLength;
        double fi = phase;
        double tmp;

        // make fi in range of 0-2*PI
        ROUND_TO_RANGE(&fi,0,2*PI,2*PI);

        // just rgb for now
        if (type == HORIZONTAL_WAVE){
            // Hor : x not changes , y changes
            for (i = 0; i < height; i++){
                uchar* dstPtr = after.scanLine(i);
                for (j = 0; j < width; j++, dstPtr++){
                    srcImage_x = j;
                    tmp = sin(w*srcImage_x +fi)*A;
                    srcImage_y = int(tmp)+i;
                    if (srcImage_y <= 0)      srcImage_y = 0;
                    if (srcImage_y >= height) srcImage_y = height-1;
                    *dstPtr = *(srcPtr + srcImage_x + srcImage_y * width);
                }
            }
        }
        else if (type == VERTICAL_WAVE){
            for (i = 0; i < height; i ++){
                uchar* dstPtr = after.scanLine(i);
                for (j = 0; j < width; j++, dstPtr++){
                    srcImage_y = i;
                    tmp = (sin(w*srcImage_y + fi))*A;
                    srcImage_x = int(tmp) + j;
                    if (srcImage_x <= 0)     srcImage_x = 1;
                    if (srcImage_x >= width) srcImage_x = width-1;
                    *dstPtr = *(srcPtr + srcImage_x + srcImage_y * width);
                }
            }
        }
        else{
            // circle wave , center must be the the image-center
            int src_cnt_x,src_cnt_y;   // center of Image : (x,y)
            int xOffset,yOffset;       // src_cnt_x - current x
            double xPart,yPart,xy;
            src_cnt_x = width/2;
            src_cnt_y = height/2;
            for (i = 0; i < height; i++){
                uchar* dstPtr = after.scanLine(i);
                for (j = 0; j < width; j++, dstPtr++){
                    xOffset = src_cnt_x - j;
                    yOffset = src_cnt_y - i;
                    xy = sqrt(xOffset * xOffset + yOffset * yOffset);
                    // assert(xy > 0);
                    if (xy == 0) xy = 1.0;
                    xPart = fabs(xOffset)/xy;
                    yPart = fabs(yOffset)/xy;
                    tmp = (sin(w*xy + fi))*A;
                    srcImage_x = int(tmp * xPart) + j;
                    srcImage_y = int(tmp * yPart) + i;
                    if (srcImage_x <= 0)      srcImage_x = 1;
                    if (srcImage_x >= width)  srcImage_x = width-1;
                    if (srcImage_y <= 0)      srcImage_y = 1;
                    if (srcImage_y >= height) srcImage_y = height-1;
                    *dstPtr = *(srcPtr + srcImage_x + srcImage_y * width);
                }
            }
        }
    }
    else
    {
        int i,j;

        // function proto f(x) = Asin(wx + fi)
        uint A = amplitude;
        assert(waveLength > 0);
        double w = 2*PI/waveLength;
        double fi = phase;
        double tmp;

        // make fi in range of 0-2*PI
        ROUND_TO_RANGE(&fi,0,2*PI,2*PI);

        // just rgb for now
        if (type == HORIZONTAL_WAVE){
            // Hor : x not changes , y changes
            for (i = 0; i < height; i++){
                QRgb* dstPtr = (QRgb*)after.scanLine(i);
                for (j = 0; j < width; j++, dstPtr++){
                    srcImage_x = j;
                    tmp = sin(w*srcImage_x +fi)*A;
                    srcImage_y = int(tmp)+i;
                    if (srcImage_y <= 0)      srcImage_y = 0;
                    if (srcImage_y >= height) srcImage_y = height-1;
                    *dstPtr = src.pixel(srcImage_x,srcImage_y);
                }
            }
        }
        else if (type == VERTICAL_WAVE){
            for (i = 0; i < height; i ++){
                QRgb* dstPtr = (QRgb*)after.scanLine(i);
                for (j = 0; j < width; j++, dstPtr++){
                    srcImage_y = i;
                    tmp = (sin(w*srcImage_y + fi))*A;
                    srcImage_x = int(tmp) + j;
                    if (srcImage_x <= 0)     srcImage_x = 1;
                    if (srcImage_x >= width) srcImage_x = width-1;
                    *dstPtr = src.pixel(srcImage_x,srcImage_y);
                }
            }
        }
        else{
            // circle wave , center must be the the image-center
            int src_cnt_x,src_cnt_y;   // center of Image : (x,y)
            int xOffset,yOffset;       // src_cnt_x - current x
            double xPart,yPart,xy;
            src_cnt_x = width/2;
            src_cnt_y = height/2;
            for (i = 0; i < height; i++){
                QRgb* dstPtr = (QRgb*)after.scanLine(i);
                for (j = 0; j < width; j++, dstPtr++){
                    xOffset = src_cnt_x - j;
                    yOffset = src_cnt_y - i;
                    xy = sqrt(xOffset * xOffset + yOffset * yOffset);
                    // assert(xy > 0);
                    if (xy == 0) xy = 1.0;
                    xPart = fabs(xOffset)/xy;
                    yPart = fabs(yOffset)/xy;
                    tmp = (sin(w*xy + fi))*A;
                    srcImage_x = int(tmp * xPart) + j;
                    srcImage_y = int(tmp * yPart) + i;
                    if (srcImage_x <= 0)      srcImage_x = 1;
                    if (srcImage_x >= width)  srcImage_x = width-1;
                    if (srcImage_y <= 0)      srcImage_y = 1;
                    if (srcImage_y >= height) srcImage_y = height-1;
                    *dstPtr = src.pixel(srcImage_x,srcImage_y);
                }
            }
        }
    }
    return after;
}

QImage applySharpen(QImage &image)
{
    int laplacianOperator[9] = {-1, -1, -1,
                                -1, 9, -1,
                                -1, -1, -1};
    int width = image.width();
    int height = image.height();

    QImage afterImage = QImage(image);

    if (image.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < height; i++)
        {
            uchar* srcPtr = image.scanLine(i);
            uchar* dstPtr = afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[3];
                int* yy = new int[3];
                int temp = 3 / 2;
                for (int k = 0; k < 3; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                int tempPixels = 0;
                int tempCount = 0;
                for (int m = 0; m < 3; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < 3; n++)
                    {
                        tempPixels += laplacianOperator[tempCount] * (*(srcPtr + wOffset + xx[n]));
                        tempCount++;
                    }
                }
                tempPixels = tempPixels > 255 ? 255 : tempPixels;
                tempPixels = tempPixels < 0 ? 0 : tempPixels;
                *dstPtr = tempPixels;
            }
        }
    }
    else
    {
        for (int i = 0; i < height; i++)
        {
            QRgb* srcPtr = (QRgb*)image.scanLine(i);
            QRgb* dstPtr = (QRgb*)afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[3];
                int* yy = new int[3];
                int temp = 3 / 2;
                for (int k = 0; k < 3; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                int tempA = 0;
                int tempR = 0;
                int tempG = 0;
                int tempB = 0;
                int tempCount = 0;
                for (int m = 0; m < 3; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < 3; n++)
                    {
                        QRgb rgb = *(srcPtr + wOffset + xx[n]);
                        int op = laplacianOperator[tempCount];
                        tempA += op * qAlpha(rgb);
                        tempR += op * qRed(rgb);
                        tempG += op * qGreen(rgb);
                        tempB += op * qBlue(rgb);
                        tempCount++;
                    }
                }
                tempA = tempA > 255 ? 255 : tempA;
                tempA = tempA < 0 ? 0 : tempA;
                tempR = tempR > 255 ? 255 : tempR;
                tempR = tempR < 0 ? 0 : tempR;
                tempG = tempG > 255 ? 255 : tempG;
                tempG = tempG < 0 ? 0 : tempG;
                tempB = tempB > 255 ? 255 : tempB;
                tempB = tempB < 0 ? 0 : tempB;
                *dstPtr = qRgba(tempR, tempG, tempB, tempA);
            }
        }
    }
    return afterImage;
}

QImage applyMosaic(int size, QImage src)
{
    QImage after = QImage(src);
    // 1. size must < after.short_edge
    // 2. size usually > 4 for a good effect
    int width = after.width();
    int height = after.height();
    assert(size < width && size < height);

    if (src.format() == QImage::Format_Indexed8)
    {
        uchar* dstPtr = after.bits();
        int blockPerLine = (int)ceil(after.width()*1.0/size);     // actually a integer
        int blockPerColu = (int)ceil(after.height()*1.0/size);
        int tx,ty;  // block_x_size and block_y_size
        int m,n;
        unsigned long long gray;
        int blockSize = 0;
        for (int i = 0; i < blockPerColu; i++){
            m = size*i;
            for (int j = 0; j < blockPerLine; j++){
                n = size*j;
                /**
                  * area is : (j*size,i*size)   (j*size+1,i*size)   ... ((j+1)*size-1,i*size)
                  *           (j*size,i*size+1) (j*size+1,i*size+1) ... ((j+1)*size-1,i*size+1)
                  *           ...               ...                 ... ...
                  *           (j*size,(i+1)*size-1)         ...
                  */
                tx = after.width() - n;    // left size
                ty = after.height() - m;
                tx = tx > size ? size : tx;
                ty = ty > size ? size : ty;
                int ix,iy;

                gray = 0;
                for (iy = 0;iy < ty; iy ++){
                    for (ix = 0;ix < tx; ix ++){
                        gray += *(src.scanLine(m+iy) + n + ix);
                    }
                }
                blockSize = tx*ty;

                gray /= blockSize;

                // make sure each color in 0-255
                COLOR_IN_RANGE((int)gray);

                for (iy = 0; iy < ty; iy ++){
                    for (ix = 0; ix < tx; ix ++){
                        *(dstPtr + (m+iy)*width + n+ix) = gray;
                    }
                }
            }
        }
    }
    else
    {
        QRgb* dstPtr = (QRgb*)after.bits();
        int blockPerLine = (int)ceil(after.width()*1.0/size);     // actually a integer
        int blockPerColu = (int)ceil(after.height()*1.0/size);
        int tx,ty;  // block_x_size and block_y_size
        int m,n;
        unsigned long long red,blue,green;
        QRgb tempRgb;
        int blockSize = 0;
        for (int i = 0; i < blockPerColu; i++){
            m = size*i;
            for (int j = 0; j < blockPerLine; j++){
                n = size*j;
                /**
                  * area is : (j*size,i*size)   (j*size+1,i*size)   ... ((j+1)*size-1,i*size)
                  *           (j*size,i*size+1) (j*size+1,i*size+1) ... ((j+1)*size-1,i*size+1)
                  *           ...               ...                 ... ...
                  *           (j*size,(i+1)*size-1)         ...
                  */
                tx = after.width() - n;    // left size
                ty = after.height() - m;
                tx = tx > size ? size : tx;
                ty = ty > size ? size : ty;
                int ix,iy;

                red = 0;
                green = 0;
                blue = 0;
                for (iy = 0;iy < ty; iy ++){
                    for (ix = 0;ix < tx; ix ++){
                        tempRgb = src.pixel(n+ix,m+iy);
                        red   += (unsigned long long)qRed(tempRgb);
                        green += (unsigned long long)qGreen(tempRgb);
                        blue  += (unsigned long long)qBlue(tempRgb);
                    }
                }
                blockSize = tx*ty;

                red   /= blockSize;
                green /= blockSize;
                blue  /= blockSize;

                // make sure each color in 0-255
                COLOR_IN_RANGE((int)red);
                COLOR_IN_RANGE((int)green);
                COLOR_IN_RANGE((int)blue);
                tempRgb = qRgb((int)red,(int)green,(int)blue);

                for (iy = 0; iy < ty; iy ++){
                    for (ix = 0; ix < tx; ix ++){
                        *(dstPtr + (m+iy)*width + n+ix) = tempRgb;
                    }
                }
            }
        }
    }

    return after;
}

QImage applyLighting(QPoint location, int intensity, int radius, QImage src)
{
    int width = src.width();
    int height = src.height();

    QImage dst = QImage(src);
    if (src.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < height; i++)
        {
            unsigned char* dstPtr = dst.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++)
            {
                double mylength = sqrt((i - location.y()) * (i - location.y()) +
                                       (j - location.x()) * (j  -location.x()));

                if (mylength < radius)
                {
                    int newpixel = intensity * (1 - mylength / radius);
                    newpixel = *dstPtr + newpixel;
                    if (newpixel > 255)
                    {
                        newpixel = 255;
                    }
                    *dstPtr = (unsigned char)newpixel;
                }
            }
        }
    }
    else
    {
        for (int i = 0; i < height; i++)
        {
            QRgb* dstPtr = (QRgb*)dst.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++)
            {
                double mylength = sqrt((i - location.y()) * (i - location.y()) +
                                       (j - location.x()) * (j  -location.x()));

                if (mylength < radius)
                {
                    int newpixel = intensity * (1 - mylength / radius);
                    int R = qRed(*dstPtr);
                    int G = qGreen(*dstPtr);
                    int B = qBlue(*dstPtr);

                    R = R + newpixel;
                    if (R > 255)
                        R = 255;
                    G = G + newpixel;
                    if (G > 255)
                        G = 255;
                    B = B + newpixel;
                    if (B > 255)
                        B = 255;

                    *dstPtr = qRgb(R, G, B);

                }
            }
        }
    }
    return dst;
}

GaussianFilter::GaussianFilter()
{
    m_radius = 1;
    m_sigma = (double)m_radius / 3;
    m_diamet = (m_radius << 1) + 1;
    m_sigma2 = 2 * m_sigma * m_sigma;
    m_nuclear = 0;
    m_gaussOperator = new double[m_diamet * m_diamet];

    computeGaussOperator();
}

void GaussianFilter::setRadius(int radius)
{
    m_nuclear = 0;
    m_radius = radius;
    m_diamet = (m_radius << 1) + 1;

    m_gaussOperator = new double[m_diamet * m_diamet];
    computeGaussOperator();
}

void GaussianFilter::setSigma(double sigma)
{
    m_nuclear = 0;
    m_sigma = sigma;
    m_sigma2 = 2 * m_sigma * m_sigma;

    computeGaussOperator();
}

void GaussianFilter::computeGaussOperator()
{
    int i = 0;
    for(long y = -m_radius; y <= m_radius; ++y)
    {
        for(long x = -m_radius; x <= m_radius; ++x)
        {
            m_gaussOperator[i] = exp(-(double)(x * x + y * y) / m_sigma2) /
                    (m_sigma2 * PI);
            m_nuclear += m_gaussOperator[i];
            i++;
        }
    }
    i = 0;
    for(long y = -m_radius; y <= m_radius; ++y)
    {
        for(long x = -m_radius; x <= m_radius; ++x)
        {
            m_gaussOperator[i] = m_gaussOperator[i] / m_nuclear;
            i++;
        }
    }
}

QImage GaussianFilter::applyGaussOperator(QImage &image)
{
    int width = image.width();
    int height = image.height();

    QImage afterImage = QImage(image);

    if (image.format() == QImage::Format_Indexed8)
    {
        for (int i = 0; i < height; i++)
        {
            uchar* srcPtr = image.scanLine(i);
            uchar* dstPtr = afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[m_diamet];
                int* yy = new int[m_diamet];
                int temp = m_diamet / 2;
                for (int k = 0; k < m_diamet; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                int tempPixel = 0;
                int count = 0;
                for (int m = 0; m < m_diamet; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < m_diamet; n++)
                    {
                        tempPixel += m_gaussOperator[count] * (*(srcPtr + wOffset + xx[n]));
                        count++;
                    }
                }
                if (tempPixel > 255)
                {
                    tempPixel = 255;
                }
                *dstPtr = tempPixel;
            }
        }
    }
    else
    {
        for (int i = 0; i < height; i++)
        {
            QRgb* srcPtr = (QRgb*)image.scanLine(i);
            QRgb* dstPtr = (QRgb*)afterImage.scanLine(i);
            for (int j = 0; j < width; j++, dstPtr++, srcPtr++)
            {
                int* xx = new int[m_diamet];
                int* yy = new int[m_diamet];
                int temp = m_diamet / 2;
                for (int k = 0; k < m_diamet; k++)
                {
                    xx[k] = k - temp;
                    yy[k] = k - temp;
                    if (i + yy[k] >= height || i + yy[k] < 0)
                    {
                        yy[k] = 0;
                    }
                    if (j + xx[k] >= width || j + xx[k] < 0)
                    {
                        xx[k] = 0;
                    }
                }

                int tempA = 0;
                int tempR = 0;
                int tempG = 0;
                int tempB = 0;
                int count = 0;
                for (int m = 0; m < m_diamet; m++)
                {
                    int wOffset = yy[m] * width;
                    for (int n = 0; n < m_diamet; n++)
                    {
                        QRgb rgb = *(srcPtr + wOffset + xx[n]);
                        tempA += m_gaussOperator[count] * qAlpha(rgb);
                        tempR += m_gaussOperator[count] * qRed(rgb);
                        tempG += m_gaussOperator[count] * qGreen(rgb);
                        tempB += m_gaussOperator[count] * qBlue(rgb);
                        count++;
                    }
                }
                tempA = tempA > 255 ? 255 : tempA;
                tempR = tempR > 255 ? 255 : tempR;
                tempG = tempG > 255 ? 255 : tempG;
                tempB = tempB > 255 ? 255 : tempB;
                *dstPtr = qRgba(tempR, tempG, tempB, tempA);
            }
        }
    }
    return afterImage;
}
